package com.tvsinfo.jdk.demo.case6_higher_order_function;

import java.util.*;
import java.util.function.*;
import java.util.stream.Collectors;

/**
 * list合并工具
 *
 * @Author ruyi.song
 * @Date 2019/12/10 09:39:31
 */
public class ListMergeUtils {


    //, BiFunction<A, B, R> abBiFunction
    public static <A, B, R> Supplier<Map<A, List<B>>> merge(List<A> forEachA, List<B> filterList, BiPredicate<A, B> filterCondition) {
        Map<A, List<B>> cdMap = new HashMap<>();
        forEachA.stream().forEach(a1 -> {
            List<B> list = filterList.stream().filter(b -> filterCondition.test(a1, b)).collect(Collectors.toList());
            cdMap.put(a1, list);
        });
        return () -> cdMap;
    }

    //, BiFunction<A, B, R> abBiFunction
    public static <A, B, R> ListMergeCollectors merge2(List<A> forEachA, List<B> filterList, BiPredicate<A, B> filterCondition) {
        Map<A, List<B>> cdMap = new HashMap<>();
        forEachA.stream().forEach(a1 -> {
            List<B> list = filterList.stream().filter(b -> filterCondition.test(a1, b)).collect(Collectors.toList());
            cdMap.put(a1, list);
        });
        return new ListMergeCollectors(cdMap);
    }

    /**
     * 原对象结果返回
     *
     * @param forEachA        合并的主列表
     * @param filterList      要条件合并的列表
     * @param filterCondition 合并条件
     * @param <A>             合并对象
     * @param <B>             合并对象
     * @return
     */
    public static <A, B> Map<A, B> toMap(List<A> forEachA,
                                         List<B> filterList,
                                         BiPredicate<A, B> filterCondition) {
        return toCustomerResult(forEachA, filterList, filterCondition,
                Function.identity(),
                Function.identity(),
                Function.identity());
    }


    /**
     * map自定义key，自定义value返回
     *
     * @param forEachA        合并的主列表
     * @param filterList      要条件合并的列表
     * @param filterCondition 合并条件
     * @param keyMapper       合并后的key
     * @param valueMapper     合并的value
     * @param <A>             合并对象
     * @param <B>             合并对象
     * @param <C>             合并key对象
     * @param <D>             合并value对象
     * @return
     */
    public static <A, B, C, D> Map<C, D> toMap(List<A> forEachA,
                                               List<B> filterList,
                                               BiPredicate<A, B> filterCondition,
                                               Function<A, C> keyMapper,
                                               Function<B, D> valueMapper) {
        return toCustomerResult(forEachA, filterList, filterCondition,
                keyMapper,
                valueMapper,
                Function.identity());
    }


    /**
     * toList
     *
     * @param forEachSource   合并的主列表
     * @param filterList      要条件合并的列表
     * @param filterCondition 合并条件
     * @param <Source>        合并对象
     * @param <Filter>        合并对象
     * @param <Result>        结果的最终合并
     * @return
     */
    public static <Source, Filter, CompareValue, Result> List<Result> toList(List<Source> forEachSource,
                                                                             List<Filter> filterList,
                                                                             Function<Source, CompareValue> value1,
                                                                             Function<Filter, CompareValue> value2,
                                                                             BiPredicate<CompareValue, CompareValue> filterCondition,
                                                                             BiFunction<Source, Filter, ? extends Result> combiner) {
        Objects.requireNonNull(forEachSource, "主列表为空");
        Objects.requireNonNull(filterList, "匹配列表为空");
        Objects.requireNonNull(filterCondition, "匹配条件为空");
        Objects.requireNonNull(combiner, "合并条件为空");

        List<Result> list = new ArrayList<>();
        forEachSource.stream().forEach(source -> {
            Optional<Filter> optional = filterList.stream().filter(filter ->
                    filterCondition.test(value1.apply(source), value2.apply(filter))
            ).findFirst();

            if (optional.isPresent()) {
                Result result = combiner.apply(source, optional.get());
                if (null != result) {
                    list.add(result);
                }
            }
        });
        return list;
    }

    /**
     * toList
     *
     * @param forEachA        合并的主列表
     * @param filterList      要条件合并的列表
     * @param filterCondition 合并条件
     * @param <A>             合并对象
     * @param <B>             合并对象
     * @param <R>             结果的最终合并
     * @return
     */
    public static <A, B, R> List<R> toList(List<A> forEachA,
                                           List<B> filterList,
                                           BiPredicate<A, B> filterCondition,
                                           BiFunction<A, B, ? extends R> combiner) {
        Objects.requireNonNull(forEachA, "主列表为空");
        Objects.requireNonNull(filterList, "匹配列表为空");
        Objects.requireNonNull(filterCondition, "匹配条件为空");
        Objects.requireNonNull(combiner, "合并条件为空");
        List<R> list = new ArrayList<>();
        forEachA.stream().forEach(a1 -> {
            Optional<B> optional = filterList.stream().filter(b -> filterCondition.test(a1, b)).findFirst();
            if (optional.isPresent()) {
                R r = combiner.apply(a1, optional.get());
                if (null != r) {
                    list.add(r);
                }
            }
        });
        return list;
    }


    /**
     * 循环读取对象
     *
     * @param forEachSource   合并的主列表
     * @param filterList      要条件合并的列表
     * @param filterCondition 合并条件
     * @param <Source>        合并对象
     * @param <Filter>        合并对象
     * @return
     */
    public static <Source, Filter> void forEachResult(List<Source> forEachSource,
                                                      List<Filter> filterList,
                                                      BiPredicate<Source, Filter> filterCondition,
                                                      BiConsumer<Source, Filter> abBiConsumer) {
        Map<Source, Filter> map = toCustomerResult(forEachSource, filterList, filterCondition,
                Function.identity(),
                Function.identity(),
                Function.identity());
        map.forEach(abBiConsumer);
    }

    /**
     * 循环读取对象
     *
     * @return
     */
    public static <Source, Filter, CompareValue> void forEachResult(List<Source> forEachSource,
                                                                    List<Filter> filterList,
                                                                    Function<Source, CompareValue> value1,
                                                                    Function<Filter, CompareValue> value2,
                                                                    BiPredicate<CompareValue, CompareValue> filterCondition,
                                                                    BiConsumer<Source, Filter> abBiConsumer) {
        forEachResult(
                forEachSource,
                filterList,
                (source, filter) -> filterCondition.test(value1.apply(source), value2.apply(filter)),
                abBiConsumer);
    }


    /**
     * 合并对象 自定义合并返回
     *
     * @param forEachSource   合并的主列表
     * @param filterList      要条件合并的列表
     * @param filterCondition 合并条件
     * @param <Source>        合并对象
     * @param <Filter>        合并对象
     * @return
     */
    public static <Source, Filter, Result> Result toCustomerResult(List<Source> forEachSource,
                                                                   List<Filter> filterList,
                                                                   BiPredicate<Source, Filter> filterCondition,
                                                                   Function<Map<Source, Filter>, Result> combiner) {
        return toCustomerResult(
                forEachSource,
                filterList,
                filterCondition,
                Function.identity(),
                Function.identity(),
                combiner);
    }

    /**
     * 合并【自定义对象】自定义结果合并返回
     *
     * @param forEachSource   合并的主列表
     * @param filterList      要条件合并的列表
     * @param filterCondition 合并条件
     * @param keyMapper       合并后的key
     * @param valueMapper     合并的value
     * @param <Source>        合并对象
     * @param <Filter>        合并对象
     * @param <SValue>        合并key对象
     * @param <FValue>        合并value对象
     * @param <Result>        结果的最终合并
     * @return
     */
    public static <Source, Filter, SValue, FValue, Result> Result toCustomerResult(List<Source> forEachSource,
                                                                                   List<Filter> filterList,
                                                                                   BiPredicate<Source, Filter> filterCondition,
                                                                                   Function<Source, SValue> keyMapper,
                                                                                   Function<Filter, FValue> valueMapper,
                                                                                   Function<Map<SValue, FValue>, Result> combiner) {
        Objects.requireNonNull(forEachSource, "主列表为空");
        Objects.requireNonNull(filterList, "匹配列表为空");
        Objects.requireNonNull(filterCondition, "匹配条件为空");
        Objects.requireNonNull(keyMapper, "key条件为空");
        Objects.requireNonNull(valueMapper, "value条件为空");
        Objects.requireNonNull(combiner, "合并条件为空");

        Map<SValue, FValue> cdMap = new HashMap<>();
        forEachSource.stream().forEach(source1 -> {
            Optional<Filter> optional = filterList.stream().filter(
                    filter -> filterCondition.test(source1, filter)
            ).findFirst();

            if (optional.isPresent()) {
                cdMap.put(keyMapper.apply(source1), valueMapper.apply(optional.get()));
            } else {
                cdMap.put(keyMapper.apply(source1), null);
            }
        });
        return combiner.apply(cdMap);
    }

}
